package com.tengju.user.infrastructure.persistence.es;

import com.tengju.user.domain.annotation.ElasticEncrypt;
import com.tengju.user.domain.service.UserDocumentTemplate;
import com.tengju.user.domain.shared.ElasticsearchIndexNameEnum;
import com.tengju.user.domain.shared.JsonUtil;
import com.tengju.user.infrastructure.shared.ElasticsearchException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * @author 李大仙
 */
@Slf4j
@Repository
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class UserDocumentTemplateImpl implements UserDocumentTemplate {

    private final RestHighLevelClient restHighLevelClient;

    /**
     * 更新字段的值
     * 注意：不支持字段加解密
     *
     * @param indexNameEnum #{@link ElasticsearchIndexNameEnum}
     * @param id
     * @param json
     */
    @Override
    public void update(ElasticsearchIndexNameEnum indexNameEnum, String id,String json) {
        try {
            UpdateRequest request = new UpdateRequest(indexNameEnum.getIndexName(), id);
            request.doc(json, XContentType.JSON);
            request.scriptedUpsert(true);
            request.docAsUpsert(true);
            request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
            restHighLevelClient.update(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            log.error("update error", e);
            throw new ElasticsearchException("update error");
        }
    }

    /**
     * 根据条件查询
     * 注意：不支持字段加解密
     *
     * @param indexNameEnum #{@link ElasticsearchIndexNameEnum}
     * @param builder       查询参数
     * @param clazz         结果类对象
     */
    @Override
    public <T> List<T> search(ElasticsearchIndexNameEnum indexNameEnum, SearchSourceBuilder builder, Class<T> clazz) {
        SearchRequest request = new SearchRequest(indexNameEnum.getIndexName());
        request.source(builder);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            List<T> res = new ArrayList<>(hits.length);
            for (SearchHit hit : hits) {
                res.add(JsonUtil.toObject(hit.getSourceAsString(), clazz));
            }
            return res;
        } catch (Exception e) {
            log.error("search error", e);
            throw new ElasticsearchException("search error");
        }
    }

    /**
     * 查询条件下共有多少条
     * 注意：不支持字段加解密
     *
     * @param indexNameEnum #{@link ElasticsearchIndexNameEnum}
     * @param builder       查询参数
     */
    @Override
    public long count(ElasticsearchIndexNameEnum indexNameEnum, QueryBuilder builder) {
        CountRequest countRequest = new CountRequest(indexNameEnum.getIndexName());
        countRequest.query(builder);
        try {
            CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
            return response.getCount();
        } catch (Exception e) {
            log.error("count error", e);
            throw new ElasticsearchException("count error");
        }
    }
}
